﻿using System;
using System.Reflection;
using OpenCompiler.CodeGen.Exceptions;
using OpenCompiler.CodeGen.Implementation;


namespace OpenCompiler.CodeGen.Emit
{
	public class ConstructionContext : IConstructionContext
	{
		private IConstructionProvider _constructionProvider;


		public ConstructionContext()
		{
			this._constructionProvider = new ConstructionProvider();
		}

		public ConstructionContext(IConstructionProvider constructionProvider)
		{
			this._constructionProvider = constructionProvider;
		}


		private void ValidateTypeame(ITypeBuilder builder)
		{
			if (string.IsNullOrEmpty(builder.Name))
			{
				throw new UnnamedTypeException();
			}
		}

		private void ValidateConstruction(ITypeBuilder builder)
		{
			this.ValidateTypeame(builder);
			
		}

		private void ImplementInterfaces(System.Reflection.Emit.TypeBuilder typeBuilder, ITypeBuilder builder)
		{
			foreach (var theInterface in builder.Interfaces)
			{
				typeBuilder.AddInterfaceImplementation(theInterface.InterfaceType);
			}
		}

		private void ImplementMethods(System.Reflection.Emit.TypeBuilder typeBuilder, ITypeBuilder builder)
		{
			foreach( var method in builder.Methods )
			{
				var body = method.ComposedBody;
				
				

			}
		}

		public Type Construct(ITypeBuilder builder)
		{
			this.ValidateConstruction(builder);
			var typeBuilder = EmitManager.Instance.DefineType(builder.Name, this.GetTypeAttributes(builder));
			this.ImplementInterfaces(typeBuilder, builder);
			this.ImplementMethods(typeBuilder,builder);

			var newType = typeBuilder.CreateType();
			return newType;
		}

		public ITypeBuilder CreateType(string name)
		{
			ITypeBuilder builder = this._constructionProvider.CreateTypeBuilder();
			return builder;
		}

		public ITypeBuilder<T> BuildFrom<T>() where T : class
		{
			ITypeBuilder<T> builder = this._constructionProvider.CreateTypeBuilderFromType<T>();
			return builder;
		}

		public T CreateInstance<T>() where T : class
		{

			return default(T);
		}

		private TypeAttributes GetTypeAttributes(ITypeBuilder builder)
		{
			TypeAttributes attributes = 0;
			attributes |= this.GetVisibility(builder, attributes);
			return attributes;
		}

		private TypeAttributes GetVisibility(ITypeBuilder builder, TypeAttributes attributes)
		{
			switch (builder.Visibility)
			{
				case Visibility.Public:
					{
						attributes |= TypeAttributes.Public;
					}
					break;
				case Visibility.Private:
					{
						attributes |= TypeAttributes.NotPublic;
					}
					break;
			}
			return attributes;
		}

	}
}
